#include "diskTest.h"
#include <locale.h>
#define CONFIRM_STRING "Yes, do as I say"
#define PTR_NEG1 (void *)-1
#define strequ(a, b) (strcmp(a, b) == 0)
#define shalloc(type) mmap(NULL, sizeof(type), PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0)
#define fork_se(collect, child)                         \
        switch (collect fork()) {                       \
                default: break;                         \
                case -1:                                \
                        perr_e("Task Fork Err."); break;\
                case 0:                                 \
                        child;                          \
        }
#ifndef _GUI
static void intr(int);
static pid_t task_pid = 0;
#endif // !_GUI
static bool *start = 0;
pid_t main_pid = 0;
bool _true = true;
bool _false = false;

static void init_localization(void);

int main(int ac, char **av) {
        init_localization();
        #ifdef _GUI
        exit(gui_main(ac, av));
        #else // !_GUI
        get_opt(ac, av);
        if (!opt.test && !opt.force) {
                char buf[30];
                printf("[" CONFIRM_STRING "]: ");
                fgets(buf, 29, stdin);
                if (!strequ(buf, CONFIRM_STRING) &&
                    !strequ(buf, CONFIRM_STRING "\n")) {
                        speak(_("Nothing to do\n"));
                        exit(0);
                }
        }
        if (opt.test && opt.random && !opt.seeded)
                error_se(_("No seed was given"));
        if (opt.logLevel == 2) {
                // Var init
                head = shalloc(size_t);
                break_point = shalloc(off_t);
                *break_point = -1;
                start = shalloc(bool);
                *start = false;
                if (!(head && break_point && start) ||
                    (head == PTR_NEG1 || break_point == PTR_NEG1 || start == PTR_NEG1))
                        perr_e(_("Mmap Err"));
                // Fork
                main_pid = getpid();
                fork_se(task_pid =, task(&_true));
                fork_se(, loop_progress());
                // SIG
                signal(SIGINT, intr);
                signal(SIGTERM, intr);
                // Already
                int exit_stat;
                *start = true;
                waitpid(task_pid, &exit_stat, 0);
                exit(exit_stat);
        } else {
                task(&_false);
        }
        #endif // !_GUI
}

#ifndef _GUI
static void intr(int _nil) {
        (void)_nil;
        kill(task_pid, SIGINT);
        exit(SIGINT);
}
#endif // !_GUI

int task(void *_mult) {
#ifdef _GUI
        break_log_write = break_log;
#else // !_GUI
        bool mult = *(bool *)_mult;
        if (mult) {
                usleep(100000); // 10ms
                if (!*start)
                        perr_e(_("Task fail to init"));
        }
        speak(_("Start\n"));
#endif // _GUI
        fseeko64(opt.fp, opt.start, SEEK_SET);
        if (opt.test) {
                fbinit(ftello64(opt.fp), true);
                if (opt.random)
                        test_seed();
                else
                        test_num();
        } else {
                fbinit(ftello64(opt.fp), false);
                if (opt.random)
                        write_random();
                else
                        write_num();
                fbflush();
        }
#ifndef _GUI
        speak(_("Finish\n"));
        exit(0);
#else // _GUI
        gworking = false;
#endif // _GUI
        return 0;
}

char *exe_file_path(char *name) {
        char path[1024];
        ssize_t len = readlink("/proc/self/exe", path, 512 + 256);
        if (len == -1)
                perr_e(_("Not Linux proc_self_exe"));
        if (len > 512 + 256 - 10)
                perr_e(_("Path maybe too long"));
        path[len] = '\0';
        if (strcmp(path, "/") == 0)
                return "/";
        char *last_path = strrchr(path, '/');
        if (last_path == NULL)
                perr_e(_("Inner Err"));
        last_path ++;
        if (strlen(name) >= 127)
                abort();
        strcpy(last_path, name);
        char *ret = malloc_se(1024);
        strcpy(ret, path);
        return ret;
}

static void init_localization(void) {
        char *loc_path = exe_file_path("locale");
        setlocale(LC_ALL, "");
        bindtextdomain("diskTest", loc_path);
        bind_textdomain_codeset("diskTest", "UTF-8");
        textdomain("diskTest");
        free(loc_path);
}
